/**
 * 文件名称: file_transfer.c
 * 摘    要: 与文件传输相关的操作函数源文件
 * 来    源: huenrong
 *
 * 当前版本: 1.0 
 * 作    者: huenrong
 * 完成日期: 2019-07-28
 **/

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

#include "file_transfer.h"
#include "../write_log/write_log.h"

unsigned char g_frame_number = 0;       // 上传数据中的帧序号(应答时也回应相同帧序号)
struct file_info g_file_info = {0};     // 记录上传文件信息


// CRC查表值
const unsigned char auchCRCHi[]=  
{
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,  
    0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,  
    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,  
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,  
    0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,  
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,  
    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,  
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,  
    0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,  
    0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,  
    0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,  
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,  
    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,  
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,  
    0x40  
};
  
const unsigned char auchCRCLo[] =  
{
    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,  
    0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,  
    0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,  
    0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,  
    0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,  
    0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,  
    0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,  
    0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,  
    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,  
    0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,  
    0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,  
    0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,  
    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,  
    0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,  
    0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,  
    0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,  
    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,  
    0x40  
};

/************************************************************************
函数名称: calc_crc16
函数功能: 计算N字节的Crc校验
函数参数: user_data: 需要计算CRC的数据
          user_data_len: 需要计算CRC的数据长度
函数返回: 计算得到的CRC
************************************************************************/
static unsigned short calc_crc16(const unsigned char *user_data, unsigned short user_data_len)  
{  
    unsigned char uchCRCHi = 0xFF;
    unsigned char uchCRCLo = 0xFF;
    unsigned short uindex = 0;

    while (user_data_len--)
    {
        uindex = uchCRCHi ^ (*user_data++);
        uchCRCHi = uchCRCLo ^ auchCRCHi[uindex];
        uchCRCLo = auchCRCLo[uindex];
    }

    return (uchCRCHi | uchCRCLo<<8);
}

/************************************************************************
函数名称: get_file_size
函数功能: 获取文件大小
函数参数: path: 文件路径
函数返回: 成功: 返回文件大小
          失败: 返回-1
************************************************************************/
static unsigned long get_file_size(const char *path)
{
	unsigned long file_size = -1;
	struct stat stat_buff = {0};

	if (stat(path, &stat_buff) < 0)
    {
		return file_size;
	}
    else
    {
		file_size = stat_buff.st_size;
	}

	return file_size;
}

/************************************************************************
函数名称: write_file
函数功能: 向文件中写入内容
函数参数: file_path: 文件路径
          write_buf: 需要写入的内容
          len: 需要写入的长度
函数返回: 成功: 返回实际写入的文件长度
          失败: 返回-1
************************************************************************/
static int write_file(const char *file_path, const char *write_buf, 
            const unsigned long len)
{
    FILE *fd = NULL;        // 文件描述符
    int ret = -1;

    fd = fopen(file_path, "ab+");     // 以读/写方式打开二进制文件, 在文件末尾追加数据
    if (NULL == fd)
    {
        perror("write_file open");

        return -1;
    }

    ret = fwrite(write_buf, 1, len, fd);      // 读取文件内容
    if (-1 == ret)
    {
        perror("write_file read");

        return -1;
    }

    fclose(fd);      // 关闭文件

    return ret;
}

/************************************************************************
函数名称: judege_transfer_result
函数功能: 判断传输结果
函数参数: 无
函数返回: 成功: 返回TRANSFER_OK
          失败: 返回TRANSFER_FAIL
************************************************************************/
static int judege_transfer_result(void)
{
    unsigned long recv_file_size = 0;       // 实际接收文件大小

    recv_file_size = get_file_size(g_file_info.file_name);
    if (g_file_info.file_size != recv_file_size)        // 接收文件大小错误
    {
        return TRANSFER_FAIL;
    }
    else        // 实际接收文件大小正确
    {
        return TRANSFER_OK;
    }
}

/************************************************************************
函数名称: recv_data_parsing
函数功能: 接收数据解析
函数参数: recv_data: 接收到的数据
          recv_data_len: 接收数据长度
          recv_data_type: 接收到的有效数据类型
函数返回: 成功: 返回0(RECV_OK)
          失败: 返回对应错误码(详见enum transfer_response)
************************************************************************/
static int recv_data_parsing(const char *recv_data, const unsigned int recv_data_len, 
                             unsigned char *recv_data_type)
{
    int ret = -1;
    unsigned short calc_crc = 0;        // 根据上传数据计算得到的CRC
    unsigned short recv_crc = 0;        // 上传数据中的CRC
    struct file_transfer t_file_transfer = {0};     // 接收数据结构体
    char recv_user_data[MAX_USER_DATA_LEN] = {0};   // 接收到的用户数据
    char *err_msg = NULL;       // 记录出错信息

    // 将接收数据拷贝到结构体, 便于判断各字段是否正确
    memcpy(&t_file_transfer, recv_data, recv_data_len);

    g_frame_number = t_file_transfer.frame_number;      // 获取上传数据的帧序号

    if (CLIENT_HEAD != t_file_transfer.frame_head)      // 帧头错误
    {
        return HEAD_ERR;
    }

    // 获取上传数据中的crc
    recv_crc = recv_data[recv_data_len - 1] << 8 | recv_data[recv_data_len - 2];
    calc_crc = calc_crc16(recv_data, (recv_data_len - 2));      // 计算本地crc
    if (recv_crc != calc_crc)       // CRC校验失败
    {
    #ifdef DEBUG
        printf("recv data crc error\n");
    #endif
        err_msg = NULL;
        err_msg = calloc(strlen("recv data crc error") + 1, 1);
        sprintf(err_msg, "recv data crc error");
        WRITE_LOG_FILE(LOG_FATAL, err_msg);

        free(err_msg);

        return CRC_ERR;
    }

    // 判断数据类型
    switch (t_file_transfer.user_data_type)
    {
        case TRANSFER_FILE_NAME:        // 接收到文件名
        {
            (*recv_data_type) = TRANSFER_FILE_NAME;        // 记录上传有效数据类型

            // 获取上传的文件名
            memset(g_file_info.file_name, 0, sizeof(g_file_info.file_name));
            memcpy(g_file_info.file_name, t_file_transfer.user_data, t_file_transfer.user_data_len);
            
            // 如果接收到的是文件名, 则创建对应文件
            ret = creat(g_file_info.file_name, 0755);
            if (-1 == ret)
            {
            #ifdef DEBUG
		        perror("creat file error");
            #endif
                err_msg = NULL;
                err_msg = calloc(strlen("creat file error: ") + strlen(strerror(errno)) + 1, 1);
                sprintf(err_msg, "creat file error: %s", strerror(errno));
                WRITE_LOG_FILE(LOG_FATAL, err_msg);
            }
            else        // 记录/打印接收到的文件名
            {
            #ifdef DEBUG
		        printf("recv file name: [%s]\n", g_file_info.file_name);
            #endif
                err_msg = NULL;
                err_msg = calloc(strlen("recv file name: ") + strlen(g_file_info.file_name) + 1, 1);
                sprintf(err_msg, "recv file name: [%s]", g_file_info.file_name);
                WRITE_LOG_FILE(LOG_INFO, err_msg);
            }

            break;
        }

        case TRANSFER_FILE_SIZE:        // 接收到文件大小
        {
            (*recv_data_type) = TRANSFER_FILE_SIZE;        // 记录上传有效数据类型

            // 获取上传的文件大小
            g_file_info.file_size = 0;
            g_file_info.file_size = ((t_file_transfer.user_data[3] << 24) & 0xFF000000) | 
                                    ((t_file_transfer.user_data[2] << 16) & 0x00FF0000) | 
                                    ((t_file_transfer.user_data[1] << 8) & 0x0000FF00) | 
                                    ((t_file_transfer.user_data[0] << 0) & 0x000000FF);

            // 记录/打印接收到的文件大小
            #ifdef DEBUG
		        printf("recv file size: [%ld]\n", g_file_info.file_size);
            #endif
                err_msg = NULL;
                err_msg = calloc(strlen("recv file size: ") + 10 + 6, 1);
                sprintf(err_msg, "recv file size: [%ld byte]", g_file_info.file_size);
                WRITE_LOG_FILE(LOG_INFO, err_msg);

            break;
        }

        case TRANSFER_FILE_DATA:        // 接收到文件内容
        {
            (*recv_data_type) = TRANSFER_FILE_DATA;        // 记录上传有效数据类型

            // printf("recv file user_data\r\n");

            // 获取上传的文件内容
            memset(recv_user_data, 0, sizeof(recv_user_data));
            memcpy(recv_user_data, t_file_transfer.user_data, t_file_transfer.user_data_len);

            // 将接收到的文件内容写入文件
            ret = write_file(g_file_info.file_name, recv_user_data, t_file_transfer.user_data_len);
            if ((-1 == ret) || (t_file_transfer.user_data_len != ret))
            {
            #ifdef DEBUG
		        perror("write file error");
            #endif
                err_msg = NULL;
                err_msg = calloc(strlen("write file error: ") + strlen(strerror(errno)) + 1, 1);
                sprintf(err_msg, "write file error: %s", strerror(errno));
                WRITE_LOG_FILE(LOG_FATAL, err_msg);
            }

            break;
        }

        case TRANSFER_FILE_END:        // 接收到文件传输结束
        {
            (*recv_data_type) = TRANSFER_FILE_END;        // 记录上传有效数据类型

            // 记录/打印接收文件传输结束信息
            #ifdef DEBUG
		        printf("recv file data end\n");
            #endif
                err_msg = NULL;
                err_msg = calloc(strlen("recv file data end") + 1, 1);
                sprintf(err_msg, "recv file data end");
                WRITE_LOG_FILE(LOG_INFO, err_msg);

            break;
        }
        
        default:
            break;
    }
    
    free(err_msg);

    return RECV_OK;
}

/************************************************************************
函数名称: send_data_package
函数功能: 发送数据打包
函数参数: send_data_type: 发送数据类型
          send_data: 打包完成的数据
          send_data_len: 打包完成的数据长度
函数返回: 无
************************************************************************/
static void send_data_package(const unsigned char send_data_type, 
                              unsigned char *send_data, unsigned long *send_data_len)
{
    int ret = -1;
    unsigned short calc_crc = 0;
    struct file_transfer t_file_transfer = {0};
    unsigned long recv_file_size = 0;       // 记录最终接收到的文件大小

    t_file_transfer.frame_head = SERVER_HEAD;       // 帧头
    t_file_transfer.frame_number = g_frame_number;  // 帧序号
    t_file_transfer.user_data_type = send_data_type;     // 数据类型
    (*send_data_len) = 3;      // 计算帧头、帧序号、数据类型的长度
    
    switch (send_data_type)     // 根据数据类型不同, 装载不同的有效数据
    {
        case TRANSFER_FILE_NAME:        // 接收到的是文件名, 则应答文件名
        {
            t_file_transfer.user_data_len = strlen(g_file_info.file_name);       // 计算有效数据长度
            memcpy(t_file_transfer.user_data, g_file_info.file_name, t_file_transfer.user_data_len);
            (*send_data_len) += (t_file_transfer.user_data_len + 2);
            
            break;
        }

        case TRANSFER_FILE_SIZE:        // 接收到的是文件大小, 则应答文件大小
        {
            t_file_transfer.user_data_len = 4;       // 计算有效数据长度
            memcpy(t_file_transfer.user_data, &g_file_info.file_size, t_file_transfer.user_data_len);
            (*send_data_len) += (t_file_transfer.user_data_len + 2);
            
            break;
        }

        case TRANSFER_FILE_DATA:        // 接收到的是文件内容, 则应答RECV_OK
        {
            t_file_transfer.user_data_len = 1;       // 计算有效数据长度
            t_file_transfer.user_data[0] = RECV_OK;
            (*send_data_len) += (t_file_transfer.user_data_len + 2);

            break;
        }

        case TRANSFER_FILE_END:        // 接收到的是文件传输结束标志, 则应答实际接收到的文件大小
        {
            ret = judege_transfer_result();

            t_file_transfer.user_data_len = 1;       // 计算有效数据长度
            t_file_transfer.user_data[0] = ret;
            (*send_data_len) += (t_file_transfer.user_data_len + 2);

            break;
        }
        
        default:
            break;
    }

    memcpy(send_data, &t_file_transfer, (*send_data_len));
    
    calc_crc = calc_crc16(send_data, (*send_data_len));

    send_data[(*send_data_len)++] = (calc_crc & 0x00FF);
    send_data[(*send_data_len)++] = (calc_crc & 0xFF00) >> 8;
}

/************************************************************************
函数名称: send_error_response
函数功能: 发送错误应答数据打包
函数参数: error_type: 错误类型
          send_data: 打包完成的数据
          send_data_len: 打包完成的数据长度
函数返回: 无
************************************************************************/
static void send_error_response_package(const unsigned char error_type, 
                                unsigned char *send_data, unsigned long *send_data_len)
{
    unsigned short calc_crc = 0;
    struct file_transfer t_file_transfer = {0};

    t_file_transfer.frame_head = SERVER_HEAD;       // 帧头
    t_file_transfer.frame_number = g_frame_number;  // 帧序号
    t_file_transfer.user_data_type = TRANSFER_FILE_DATA;     // 数据类型
    (*send_data_len) = 3;      // 计算帧头、帧序号、数据类型的长度

    t_file_transfer.user_data_len = 1;       // 计算有效数据长度
    t_file_transfer.user_data[0] = error_type;
    (*send_data_len) += (t_file_transfer.user_data_len + 2);

    memcpy(send_data, &t_file_transfer, (*send_data_len));
    
    calc_crc = calc_crc16(send_data, (*send_data_len));

    send_data[(*send_data_len)++] = (calc_crc & 0x00FF);
    send_data[(*send_data_len)++] = (calc_crc & 0xFF00) >> 8;
}

/************************************************************************
函数名称: file_transfer_server_function
函数功能: 文件传输服务器功能函数
函数参数: fd: 客户端套接字
函数返回: 成功: 返回0
          失败: 返回-1
************************************************************************/
int file_transfer_server_function(const int *fd)
{
    int ret = -1;           // 函数返回值
    int client_fd = *fd;    // 客户端套接字
    long recv_data_len = -1;       // 接收到的数据长度
    unsigned char recv_data_type = 0;       // 接收到的有效数据类型
    unsigned long send_data_len = -1;       // 发送数据长度
    char recv_data[MAX_USER_DATA_LEN + 7] = {0};        // 接收数据
    char send_data[MAX_USER_DATA_LEN + 7] = {0};        // 发送数据
    char *err_msg = NULL;       // 记录出错信息

    while(1)
    {
        memset(recv_data, 0, sizeof(recv_data));
        recv_data_len = recv(client_fd, recv_data, sizeof(recv_data), 0);      // 接收客户端发送过来的数据
        if (recv_data_len > 0)        // 接收到数据
        {
        #if 0
            // 打印接收数据
            printf("file_transfer_server recv_data_len =  %d\r\n", recv_data_len);
            for (int i=0; i<recv_data_len; i++)
            {
                printf("0x%02X ", recv_data[i]);
            }
            printf("\r\n");
        #endif
            // 解析接收数据
            ret = recv_data_parsing(recv_data, recv_data_len, &recv_data_type);
            send_data_len = 0;
            memset(send_data, 0, sizeof(send_data));
            if (RECV_OK == ret)     // 解析接收数据成功
            {
                send_data_package(recv_data_type, send_data, &send_data_len);
            }
            else if (HEAD_ERR)      // 接收数据帧头错误
            {
                send_error_response_package(HEAD_ERR, send_data, &send_data_len);
            }
            else if (CRC_ERR)       // 接收数据CRC错误
            {
                send_error_response_package(CRC_ERR, send_data, &send_data_len);
            }
            else        // 其它错误
            {
                send_error_response_package(OTHER_ERR, send_data, &send_data_len);
            }
        #if 0
            // 打印发送数据
            printf("send_data_len = %d\r\n", send_data_len);
            for (int i=0; i<send_data_len; i++)
            {
                printf("0x%02X ", send_data[i]);
            }
            printf("\r\n");
        #endif

            send(client_fd, send_data, send_data_len, 0);       // 发送应答数据
        }
        else if (0 == recv_data_len)      // socket 正常关闭
        {
        #ifdef DEBUG
            printf("client socket closed\n");
        #endif
            err_msg = NULL;
            err_msg = calloc(strlen("client socket closed") + 1, 1);
            sprintf(err_msg, "client socket closed");
            WRITE_LOG_FILE(LOG_INFO, err_msg);
            free(err_msg);

            // 升级完成后通过文件大小判断升级是否正确(该方式可能不完美)
            ret = judege_transfer_result();
            if (TRANSFER_OK == ret)        // 接收文件成功
            {
            #ifdef DEBUG
                printf("file transfer successful\n");
            #endif
                err_msg = NULL;
                err_msg = calloc(strlen("file transfer successful") + 1, 1);
                sprintf(err_msg, "file transfer successful");
                WRITE_LOG_FILE(LOG_INFO, err_msg);
                free(err_msg);

                // 接收正确后需要进行一些升级替换处理
            }
            else        // 实际接收文件失败
            {
            #ifdef DEBUG
                printf("file transfer fail\n");
            #endif
                err_msg = NULL;
                err_msg = calloc(strlen("file transfer fail") + 1, 1);
                sprintf(err_msg, "file transfer fail");
                WRITE_LOG_FILE(LOG_INFO, err_msg);
                free(err_msg);
            }

            break;      // 退出，关闭socket
        }
        else if (-1 == recv_data_len)     // 非阻塞模式下表示缓冲区空
        {
        }
        else
        {
            // 错误处理
            if (EAGAIN != errno)
            {
            #ifdef DEBUG
                printf("recv error = %d", recv_data_len);
            #endif

                err_msg = NULL;
                err_msg = calloc(strlen("recv error = ") + 5 + 1, 1);
                sprintf(err_msg, "recv error = %d", recv_data_len);
                WRITE_LOG_FILE(LOG_INFO, err_msg);
                free(err_msg);

                break;      // 退出，关闭socket
            }
        }
    }

    close(client_fd);        // 当前连接断开后，关闭套接字

    return 0;
}

